Chapter 3
The Windows Common Controls

by Rob McGregor

In This Chapter

  Initializing and Using the Common Controls 80
  Notifications for Windows Common Controls 81
  Hot Key Controls: Class CHotKeyCtrl 85
  Spin Controls: Class CSpinButtonCtrl 88
  Slider Controls: Class CSliderCtrl 95
  Progress Bar Controls: Class CProgressCtrl 105
  Image Lists: Class CImageList 107
  List View Controls: Class CListCtrl 110
  List View Items and Subitems 114
  Tree View Controls: Class CTreeCtrl 120
  Tab Controls: Class CTabCtrl 125
  Animate Controls: Class CanimateCtrl 131
  Rich Edit Controls: Class CRichEditCtrl 135

Windows controls, those doodads and widgets that make the Windows GUI so appealing, include both the Windows standard controls and the Windows common controls. The Windows standard controls include list boxes, edit boxes, combo boxes, scrollbars, and various types of buttons. The Windows common controls were introduced with Windows 95, and they add a lot of additional GUI power to the Windows programmer’s toolkit.

Because the common controls are specific to Win32, the MFC common control classes are available only to programs running under Windows 95 or later, Windows NT version 3.51 or later, and Windows 3.1x with Win32s 1.3 or later.

Initializing and Using the Common Controls

Before you can use any of the Windows common controls in your applications, you must initialize the common control DLLs with a Win32 API function call. Windows 95 applications use the InitCommonControls() function to register and initialize all the common control window classes. Its use is easy, as you can see by its function prototype:

void InitCommonControls(VOID);

The InitCommonControls() function is now obsolete; it has been replaced with the more intelligent InitCommonControlsEx() function, which loads only the specific control classes that you specify. Its function prototype looks like this:

BOOL InitCommonControlsEx(LPINITCOMMONCONTROLSEX lpInitCtrls);

InitCommonControlsEx() registers specific common control classes, specified by the parameter lpInitCtrls, from the common control dynamic-link library (DLL). The lpInitCtrls parameter is of type LPINITCOMMONCONTROLSEX, which is a structure containing information about which control classes to load from the common control DLL.

The INITCOMMONCONTROLSEX structure looks like this:

typedef struct tagINITCOMMONCONTROLSEX
{
   DWORD dwSize;
   DWORD dwICC;
}
INITCOMMONCONTROLSEX, *LPINITCOMMONCONTROLSEX;

The parameters are as follows:

  dwSize The size of the structure, in bytes.
  dwICC The set of bit flags that specifies which common control classes to load from the common control DLL. This parameter can be any combination of the values in Table 3.1.
Table 3.1 The Bit Flags Used by the INITCOMMONCONTROLSEX Structure

Bit Flags Name Meaning

ICC_ANIMATE_CLASS Loads the animate control class.
ICC_BAR_CLASSES Loads the toolbar, status bar, trackbar, and tooltip control classes.
ICC_COOL_CLASSES Loads the rebar control class.
ICC_DATE_CLASSES Loads the date and time picker control class.
ICC_HOTKEY_CLASS Loads the hot key control class.
ICC_INTERNET_CLASSES Loads the IP address class.
ICC_LISTVIEW_CLASSES Loads the list view and header control classes.
ICC_PAGESCROLLER_CLASS Loads the pager control class.
ICC_PROGRESS_CLASS Loads the progress bar control class.
ICC_TAB_CLASSES Loads the tab and tooltip control classes.
ICC_TREEVIEW_CLASSES Loads the tree view and tooltip control classes.
ICC_UPDOWN_CLASS Loads the up-down control class.
ICC_USEREX_CLASSES Load the ComboBoxEx class.
ICC_WIN95_CLASSES Loads the animate control, header, hot key, list view, progress bar, status bar, tab, tooltip, toolbar, trackbar, tree view, and up-down control classes.



Notifications for Windows Common Controls

Windows common controls use a different messaging system than do standard Windows controls. This section takes a look at just what’s going on in the new messaging system and demonstrates how to tap into common control communications.

Each of the Win32 common controls has a corresponding set of notification codes defined for its control type. In addition to these codes, there is a set of codes shared by all the common controls. These notifications all pass a pointer to an NMHDR structure (described in the following section), and are shown in Table 3.2.

Table 3.2 Notification Codes Used by All the Win32 Common Controls

Notification Code Meaning

NM_CLICK Sent when a user clicks the left mouse button within the control.
NM_DBLCLK Sent when a user double-clicks the left mouse button within the control.
NM_KILLFOCUS Sent when the control loses the input focus.
NM_OUTOFMEMORY Sent when the control can’t finish an operation because of insufficient free memory.
NM_RCLICK Sent when a user clicks the right mouse button within the control.
NM_RDBLCLK Sent when a user double-clicks the right mouse button within the control.
NM_RETURN Sent when a user presses the Enter key and the control has the input focus.
NM_SETFOCUS Sent when the control receives the input focus.

The Notification Message Structure

Win32 common controls use a special structure to send notification messages: the NMHDR structure. This structure contains the window handle of the sender, the control ID of the sender, and the notification code being sent. The NMHDR structure looks like this:

typedef struct tagNMHDR
{
    HWND hwndFrom;  // Window handle of the sender
    UINT idFrom;    // Control ID
    UINT code;      // Notification code
} NMHDR;

Overview of the Notification Process

Win32 common controls send most notification messages as WM_NOTIFY messages; Windows standard controls (also used by 16-bit Windows) send most notification messages as WM_COMMAND messages.


Note:  

The WM_NOTIFY message isn’t used for 16-bit Windows because the message was designed specifically for 32-bit Windows.


WM_NOTIFY provides a standard way for the Win32 common controls to communicate information about control activities to Windows and to your applications (as opposed to creating a huge number of new WM_* macros for each new control).

A Win32 common control sends WM_NOTIFY notification messages to its parent window, which is usually a CDialog-derived class; in response, MFC calls the CWnd::OnNotify() method to process these messages. To intercept and handle these messages for a common control, you can override the CWnd::OnNotify() method for the control’s owner class. The prototype for CWnd::OnNotify() looks like this:

BOOL CWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)

To discover the reason for the occurrence of the WM_NOTIFY message (or, “What’s the user doing with this control?”), you must look closely at the parameters of the CWnd::OnNotify() method:

  The wParam parameter is the control ID of the control that sent the message or is NULL if the message didn’t come from a control.
  The lParam parameter is a pointer to a notification message structure (NMHDR) that contains the current notification code along with some additional information.
  The pResult parameter is a pointer to an LRESULT variable that stores the result code if the message is handled.

If the NMHDR structure is actually a member of a larger structure (as it is for most Win32 common controls), you must cast it to an NMHDR when you use it; only a few common controls actually use the simple NMHDR structure.

The lParam parameter can be a pointer to either an NMHDR structure or some other structure that has an NMHDR structure embedded as its first data member and has been typecast to an NMHDR structure.

Usually, the pointer is to a larger structure containing an NMHDR structure and not to the NMHDR structure itself. In these cases, because the NMHDR structure is the first member of the larger structure, it can be successfully typecast to an NMHDR structure.

A Better Notification Handling Scheme

As stated earlier in this chapter, the CWnd::OnNotify() method handles notification messages. Although you can do it, you really shouldn’t override the CWnd::OnNotify() method to receive notifications from controls—there’s no need to do so in most cases. Instead, you should provide a message handler method to trap the notification and add a corresponding message map entry in a control’s parent class.

The ON_NOTIFY message map macro uses this syntax:

ON_NOTIFY(NotifyCode, ControlID, ClassMethod)

In this syntax, NotifyCode represents the notification code being sent, ControlID represents the control identifier the parent window uses to communicate with the control that sent notification, and ClassMethod represents the message handler method called in response to the notification.

Your message handler method must be declared using this prototype format:

afx_msg void ClassMethod(NMHDR* pNotifyStruct, LRESULT* result);



In this syntax pNotifyStruct is a pointer to an NMHDR structure, and result is a pointer to the result code your message handler method sets.

Specifying Notification Ranges with ON_NOTIFY_RANGE

To allow several controls to process a notification using the same message handler method, you can use the ON_NOTIFY_RANGE macro in the message map instead of using the ON_NOTIFY macro. When you use ON_NOTIFY_RANGE, you must specify the beginning and ending control IDs for the controls that will have access to the notification message.


Caution:  

The control IDs used for the ON_NOTIFY_RANGE macro must be numerically contiguous. For example, if three CHeader controls are to use the same notification message handler, these control IDs would work:

// Contiguous
#define IDC_HEADER1  100
#define IDC_HEADER2  101
#define IDC_HEADER3  102

These control IDs wouldn’t work:

// Non-contiguous
#define IDC_HEADER1  100
#define IDC_HEADER2  103
#define IDC_HEADER3  108

The ON_NOTIFY_RANGE message map macro uses this format:

ON_NOTIFY_RANGE(NotifyCode, FirstCtrlID, LastCtrlID, ClassMethod)

In this syntax, NotifyCode represents the notification code being sent, ControlIDFirst represents the control identifier of the first control in the contiguous range, ControlIDFirst represents the control identifier of the last control in the contiguous range, and ClassMethod represents the message handler method called in response to the notification.

The message handler method prototype in the owner’s class declaration is as follows:

afx_msg void ClassMethod(UINT ControlID, NMHDR* pNotifyStruct,
   LRESULT* result);

In this syntax, ControlID is the identifier of the control that sent the notification, pNotifyStruct is a pointer to an NMHDR structure, and result is a pointer to the result code your message handler method sets when it processes the notification.

The new common controls provide a more structured method of notification messaging that takes some getting used to, but makes MFC programs more readable and easier to maintain in the long run. Now let’s take a look at Windows common controls and see how MFC makes using them fairly easy.

Hot Key Controls: Class CHotKeyCtrl

A hot key is a key combination used to perform some action quickly. A hot key control is a window that stores the virtual key code and shift state flags that represent a hot key. The window displays the key combination as a text string (for example, Alt+X). The control doesn’t actually set the hot key, however; your code must do this explicitly. To enable a hot key, your application must get the hot key’s values and associate these values with either a window or a thread.

MFC provides the services of a Windows hot key common control in the class CHotKeyCtrl, which is derived directly from CWnd (and therefore inherits all the functionality of CWnd). The CHotKeyCtrl class is defined in AFXCMN.H. A hot key control can be created as a child control of any window by writing code; it can also be defined in a dialog resource template. MFC automatically attaches a Windows hot key common control to a CHotKeyCtrl object when the object is created.

CHotKeyCtrl Class Methods

The CHotKeyCtrl class offers a minimal set of methods for manipulating the control and its data. The constructor, CHotKeyCtrl::CHotKeyCtrl(), allocates a CHotKeyCtrl object that is initialized with the CHotKeyCtrl::Create() method. Table 3.3 describes the class’s methods.

Table 3.3 CHotKeyCtrl Class Methods

Method Description

GetHotKey() Gets the virtual-key code and modifier flags of a hot key from a hot key control.
SetHotKey() Sets the hot key combination for a hot key control.
SetRules() Defines invalid key combinations and the default modifier combination for a hot key control.

Creating and Initializing a CHotKeyCtrl Object

To create a CHotKeyCtrl object, you use the two-step construction process typical of MFC:

1.  Call the class constructor CHotKeyCtrl::CHotKeyCtrl() to allocate the object.
2.  Initialize the CHotKeyCtrl object and attach an actual Windows hot key common control to it with a call to the CHotKeyCtrl::Create() method.

The prototype for the CHotKeyCtrl::Create() method is shown here:

BOOL Create(DWORD dwStyle, const RECT& rect,
            CWnd* pParentWnd, UINT nID);

In this syntax, the parameters are defined as follows:

  dwStyle Specifies the window style for the control.
  rect The rectangle specifying the size and position of the control.
  pParentWnd A pointer to the owner of the control.
  nID The control ID used by the parent to communicate with the control.

Using a Hot Key Control

After a hot key control is created, a default hot key value can be set by calling the SetHotKey() method. To prevent specific shift states, call the SetRules() method. A user can choose a hot key combination when the control has the focus, and an application uses the GetHotKey() method to get the virtual key and shift state values from the hot key control.

Armed with the details of the selected key combination, you can set the actual hot key by doing one of the following:

  Set up a global hot key for activating a top-level window by using the CWnd::SendMessage() method, sending a WM_SETHOTKEY message to the window to be activated.
  Set up a thread-specific hot key by calling the Win32 API function RegisterHotKey().



Global Hot Keys

A global hot key enables a user to activate a top-level window from any part of the system. To set a global hot key for a specific window, you must send the WM_SETHOTKEY message to that window. Assume that m_pHotKey is a pointer to a CHotKeyCtrl object and that pWnd is a pointer to the target window. When the hot key is activated, you can associate m_pHotKey with pWnd like this:

WORD wKeyShift = m_pHotKey->GetHotKey();
pWnd->SendMessage(WM_SETHOTKEY, wKeyShift);

Thread-Specific Hot Keys

A thread-specific hot key enables a user to activate a top-level window that was created by the current thread. To set a thread-specific hot key for a particular window, you must call the Win32 API function RegisterHotKey(). This function has the following prototype:

BOOL RegisterHotKey(HWND hWnd, int id, UINT fsModifiers, UINT vk);

In this syntax, hWnd identifies the window to receive the WM_HOTKEY messages generated by the hot key. The id parameter is the control ID of the hot key, which must be in the range of 0x0000 through 0xBFFF for an application, or 0xC000 through 0xFFFF for a shared DLL. The fsModifiers parameter is the modifier keys that must be pressed in combination with the key specified by the vk parameter to generate the WM_HOTKEY message. This can be a combination of the values shown in Table 3.4. The vk parameter is the virtual-key code of the hot key.

Table 3.4 The Modifier Flags Used with RegisterHotKey()

Value Meaning

MOD_ALT The Alt key must be held down.
MOD_CONTROL The Ctrl key must be held down.
MOD_SHIFT The Shift key must be held down.


Note:  

If the key combination specified for a hot key has already been registered by another hot key, the call to RegisterHotKey() fails.


Spin Controls: Class CSpinButtonCtrl

A spin control (or up-down control) is a friendly little control sporting a matching set of two linked arrow buttons. These controls are often found hanging around with their buddies, trying to get information from a user. Seriously, spin controls do usually interact intimately with another Windows control, and the spin control’s arrow buttons increment or decrement an internal spin control value (called the current position) when clicked. This value can be displayed as a number in a buddy window, which is most often an edit control used to get numeric input from a user.

A spin control and its buddy window (if used) often look and act like a single control. The spin control can align itself with its buddy window automatically, and it can send its current scroll position to the buddy, changing the buddy’s window text accordingly. The current position is limited to an application-defined minimum and maximum range of values. Figure 3.1 shows the typical use of a spin control: getting a value from the user.


Figure 3.1  Typical spin controls with their buddies, conspiring to get values from a user.

A spin control doesn’t need a buddy control; it can be used for many things all by itself, very much like a minimalist scroll bar. MFC provides the services of a Windows spin common control in the class CSpinButtonCtrl. Like other MFC controls, CSpinButtonCtrl is derived directly from CWnd and inherits all the functionality of CWnd.

A spin control can be created as a child control of any window by writing code, or it can be defined for use in a dialog resource template. A spin control sends Windows notification messages to its owner (usually a CDialog-derived class), and these messages can be trapped and handled by writing message map entries and message-handler methods for each message. These message map entries and methods are implemented in the spin control’s parent class.

Spin Control Styles

Like all windows, spin controls can use the general window styles available to CWnd. In addition, they use the spin styles shown in Table 3.5 (as defined in AFXCMN.H). A spin control’s styles determine its appearance and operations. Style bits are typically set when the control is initialized with the CSpinButtonCtrl::Create() method.

Table 3.5 The Window Styles Defined for a Spin Control

Style Macro Meaning

UDS_ALIGNLEFT Aligns a spin control to the left edge of the buddy window.
UDS_ALIGNRIGHT Aligns a spin control to the right edge of the buddy window.
UDS_ARROWKEYS Allows the control to increment and decrement the current position when the up-arrow and down-arrow keys are pressed on the keyboard.
UDS_AUTOBUDDY Automatically selects a buddy window by choosing the previous window in the Z-order.
UDS_HORZ Makes the control’s arrows point left and right instead of up and down.
UDS_NOTHOUSANDS Prevents the thousands separator between every three decimal digits.
UDS_SETBUDDYINT Tells the control to set the text of the buddy window when the current position changes. The text represents the current position formatted as a decimal or hexadecimal string.
UDS_WRAP Allows the current position to wrap around. Values above the maximum wrap around to start back at the minimum of the scroll range, and vice versa.


Note:  

The spin button styles all have the UDS_* prefix. This is an indication of the SDK name for this control (and the name many people use): the up-down control (Up-Down Styles—UDS). But because the MFC team at Microsoft wrapped this control into a class called a spin button control, that’s what I’ll call it, too.




CSpinButtonCtrl Messages

Because MFC wraps the Windows spin control messages (such as UDM_GETRANGE or UDM_SETBUDDY) into CSpinButtonCtrl class methods, an MFC program usually only has to handle notification messages. These messages can be trapped and handled by writing message map entries and message-handler methods for each message. You map the spin control messages to class methods by creating message map entries in the control’s parent class. Table 3.6 shows the message map entries for spin control messages.

Table 3.6 Message Map Entries for Spin Button Control Messages

Message Map Entry Meaning

ON_WM_HSCROLL Sent by a spin control with the UDS_HORZ style when the arrow buttons are clicked.
ON_WM_VSCROLL Sent by a spin control with the UDS_VERT style (the default) when the arrow buttons are clicked.
ON_EN_UPDATE Sent by a buddy edit control when the text is changed.

CSpinButtonCtrl Class Methods

The CSpinButtonCtrl class offers a concise set of methods for manipulating the control and its data. Because the MFC help files shipped with your compiler contain all the CSpinButtonCtrl class method declarations and detailed descriptions of their parameters, you won’t find detailed descriptions here, but I will provide an overview of each method so that you know what to look for when you need it.

The CSpinButtonCtrl constructor, CSpinButtonCtrl::CSpinButtonCtrl(), allocates a spin control object that is initialized with the CSpinButtonCtrl::Create() method to set attributes and ownership. The methods listed in Table 3.7 describe the methods used to get and set control attributes.

Table 3.7 CSpinButtonCtrl Class Methods

Method Description

GetAccel() Retrieves acceleration information for a spin control.
GetBase() Retrieves the current base for a spin control.
GetBuddy() Retrieves a pointer to the current buddy window.
GetPos() Retrieves the current position of a spin control.
GetRange() Retrieves the upper and lower limits (range) for a spin control.
SetAccel() Sets the acceleration for a spin control.
SetBase() Sets the base for a spin control.
SetBuddy() Sets the buddy window for a spin control.
SetPos() Sets the current position for the control.
SetRange() Sets the upper and lower limits (range) for a spin control.

Creating and Initializing a Spin Control

A CSpinButtonCtrl object, like most MFC objects, uses a two-step construction process. To create a spin control, perform the following steps:

1.  Allocate an instance of a CSpinButtonCtrl object by calling the constructor CSpinButtonCtrl::CSpinButtonCtrl() using the C++ keyword new.
2.  Initialize the CSpinButtonCtrl object and attach a Windows spin button common control to it with the CSpinButtonCtrl::Create() method to set the spin control’s parameters and styles.

For example, a CSpinButtonCtrl object is allocated and a pointer to that object is returned with this code:

CSpinButtonCtrl* pMySpinner = new CSpinButtonCtrl ;

The pointer pMySpinner is then initialized with a call to the CSpinButtonCtrl::Create() method. This method is declared as follows:

BOOL Create(DWORD dwStyle, const RECT& rect,
            CWnd* pParentWnd, UINT nID);

The first parameter, dwStyle, specifies the window style for the spin control. The window style can be any combination of the general window styles and the special spin control styles listed in Table 3.5, earlier in this chapter. The second parameter, rect, is the rectangle specifying the size and position of the control. The parameter pParentWnd is a pointer to the owner of the control, and nID is the control ID used by the parent to communicate with the spin control.

Sample Program: SPIN1

Now let’s take a look at a basic MFC program (SPIN1.EXE on the CD-ROM) that creates and displays several spin controls and buddy windows in a frame window (see Figure 3.2).


Figure 3.2  The SPIN1 frame window with three child spin controls and their buddy controls.

The SPIN1 program uses three spin controls to set the RGB color components of the client area window color, and uses both left-aligned and right-aligned buddy windows.

Examining the SPIN1 Application Header (SPIN1.H)

The header file for the SPIN1 program begins by defining window styles for the frame window’s child controls:

// Define some spin window styles
#define SBS_LEFT  (WS_VISCHILD | UDS_ALIGNLEFT | UDS_SETBUDDYINT)
#define SBS_RIGHT (WS_VISCHILD | UDS_ALIGNRIGHT | UDS_SETBUDDYINT)

// Buddy control style
#define ES_SINGLE (WS_VISCHILD | ES_LEFT | WS_BORDER)



Then the control IDs for the child window controls in this program are defined, as are the two classes used in this application: CSpinApp and CMainWnd.

The first class is the application class CSpinApp, which is a descendant of CWinApp and simply overrides the inherited InitInstance() method to provide custom application initialization.

The second class is CMainWnd, derived from CMainFrame. This class contains pointers to the child windows as class data members. These child windows consist of three spin controls and three edit controls. The class also provides the UpdateClientColor() helper method for changing the frame window’s client area color as well as these two message-handler methods:

// Message handlers
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnBuddyUpdate();

Finally, the DECLARE_MESSAGE_MAP() macro is used to set up message handling for the class.

Implementing the SPIN1 Program (SPIN1.CPP)

The first order of business is setting up the message map for the CMainWnd class. This message map contains four entries that correspond to the four message-handler method prototypes given in the class definition:

// Message map for CMainWnd
BEGIN_MESSAGE_MAP(CMainWnd, CMainFrame)
   ON_WM_SIZE()
   ON_EN_UPDATE(IDC_BUDDY1, OnBuddyUpdate)
   ON_EN_UPDATE(IDC_BUDDY2, OnBuddyUpdate)
   ON_EN_UPDATE(IDC_BUDDY3, OnBuddyUpdate)
END_MESSAGE_MAP()

Notice that the CEdit buddy windows all use the same message handler to process update messages. The class constructor initializes all child control pointers to NULL, and the class destructor destroys any allocated child objects.

CMainWnd::CreateChildControls() allocates and initializes the child controls. After this, the spin controls are each assigned a buddy edit window that displays their current position value:

// Set buddies
m_pSpin1->SetBuddy(m_pBuddy1);
m_pSpin2->SetBuddy(m_pBuddy2);
m_pSpin3->SetBuddy(m_pBuddy3);

The spin buttons then receive a scroll range of 0 to 255, the possible range of byte values used by the RGB macro (that macro is used to change the frame window’s client area color later):

// Set scroll ranges
m_pSpin1->SetRange(0, 255);
m_pSpin2->SetRange(0, 255);
m_pSpin3->SetRange(0, 255);

As a final step in initializing the spin controls, their individual current positions are set to the halfway position, at 128:

// Set current position
m_pSpin1->SetPos(128);
m_pSpin2->SetPos(128);
m_pSpin3->SetPos(128);

The CMainWnd::OnSize() message handler is quite simple, consisting of a call to the inherited method that it overrides, and another call to CMainWnd::UpdateClientColor() to repaint the client area in the color specified by the spin button positions:

void CMainWnd::OnSize(UINT nType, int cx, int cy)
{
   // Call inherited method
   CWnd::OnSize(nType, cx, cy);

   // Repaint the window at the new size
   UpdateClientColor();
}

The CMainWnd::UpdateClientColor() method is the heart of the program, reading the values from the buddy windows and converting them to the byte values needed for the color components of the RGB macro. Three CString local variables are declared to hold these values: szBuddy1Text, szBuddy2Text, and szBuddy3Text. The values are retrieved with a call to the CWnd::GetWindowText() method, returning the text from the edit controls:

m_pBuddy1->GetWindowText(szBuddy1Text);
m_pBuddy2->GetWindowText(szBuddy2Text);
m_pBuddy3->GetWindowText(szBuddy3Text);

These strings are converted to integers by calling the CMainFrame::StringToInt() method; the result is stored in three local integer variables:

INT nBuddy1 = StringToInt(szBuddy1Text);
INT nBuddy2 = StringToInt(szBuddy2Text);
INT nBuddy3 = StringToInt(szBuddy3Text);

A CBrush object is declared and initialized using these integers in an RGB macro to create a new brush of the desired color:

CBrush br(RGB(nBuddy1, nBuddy2, nBuddy3));

To finish off the method (and the SPIN1 program), the frame window’s client area is retrieved into a CRect object and passed to the CWnd::FillRect() method, along with the new brush, to specify the fill color:

CRect rcClient;
GetClientRect(&rcClient);

CBrush br(RGB(nBuddy1, nBuddy2, nBuddy3));
GetDC()->FillRect(&rcClient, &br);


Note:  

For more information about brushes, rectangles, and device contexts such as those used in the SPIN1 program, read Chapter 4, “Painting, Device Contexts, Bitmaps, and Fonts.”


Slider Controls: Class CSliderCtrl

Similar to a scroll bar, a slider control (or trackbar) is an interactive, highly visual control consisting of a slider box that runs along the length of the control, and optional tick marks that delineate range values. The slider control also has a built-in keyboard interface that allows movement of the slider with the arrow keys on the keyboard. Figure 3.3 shows a typical use of slider controls in the Windows Volume Control applet.


Figure 3.3  Slider controls provide instant visual feedback as the main user-interface component in this applet.

MFC provides the services of a Windows slider common control in the class CSliderCtrl. Like other MFC controls, CSliderCtrl is derived directly from CWnd and inherits all the functionality of CWnd. A slider control can be created as a window’s child control by writing code; alternatively, it can be defined for use in a dialog resource template.

A slider control sends Windows notification messages to its owner (usually a CDialog-derived class), and these messages can be trapped and handled by writing message map entries and message-handler methods for each message. These message map entries and methods are implemented in the slider control’s parent class.

Slider Control Styles

Like all windows, slider controls can use the general window styles available to CWnd. In addition, they use the slider control styles shown in Table 3.8 (as defined in AFXWIN.H). A slider control’s styles determine its appearance and operations. Style bits are typically set when the control is initialized with the CSliderCtrl::Create() method. Slider controls can be oriented either horizontally or vertically and can have tick marks on one side, both sides, or no tick marks at all (depending on the following styles).

Table 3.8 The Window Styles Defined for a Slider Control

Style Macro Meaning

TBS_AUTOTICKS Gives a slider tick marks for each increment in its range of values. Tick marks are automatically created with a call to the SetRange() method.
TBS_BOTH Puts tick marks on both sides of a slider control, no matter what its orientation.
TBS_BOTTOM Puts tick marks on the bottom of a horizontal slider control.
TBS_ENABLESELRANGE Gives a slider tick marks in the shape of triangles that indicate the starting and ending positions of a selected range.
TBS_HORZ Orients a slider horizontally (the default).
TBS_LEFT Puts tick marks on the left side of a vertical slider control.
TBS_NOTICKS A slider won’t have tick marks.
TBS_RIGHT Puts tick marks on the right side of a vertical slider control.
TBS_TOP Puts tick marks on the top of a horizontal slider control.
TBS_VERT Orients a slider vertically.


Note:  

These styles all have the TBS_* prefix. Like the spin button control’s UDS_* prefix, the TBS_* prefix is an indication of the SDK name of the slider control (and the name many people use): the Track Bar control (Track Bar Styles—TBS). But because the MFC team at Microsoft has wrapped this control into a class called a slider control, I refer to it as a slider, too.




CSliderCtrl Messages

An MFC program usually has to handle only two notification messages from slider controls. A slider control sends its parent window notifications of user actions in the form of scroll messages (WM_HSCROLL and WM_VSCROLL), just like a scroll bar control. These messages can be trapped and handled by writing message map entries and message-handler methods for each message. You map the slider control messages to class methods by creating message map entries in the control’s parent class. Table 3.9 shows the message map entries for slider control messages.

Table 3.9 The Two Message Map Entries for Slider Control Messages

Message Map Entry Meaning

ON_WM_HSCROLL Sent by a slider control with the TBS_HORZ style when the arrow buttons are clicked.
ON_WM_VSCROLL Sent by a slider control with the TBS_VERT style (the default) when the arrow buttons are clicked.

But there is more to the messaging than a few simple WM_* messages. The ON_WM_HSCROLL and ON_WM_VSCROLL messages have some interesting information hidden away inside them. MFC gives you access to this information through the OnHScroll() and OnVScroll() methods, which are called by MFC when a user clicks a slider’s tick marks, drags the slider box, or uses the keyboard arrows to otherwise control slider movement. These scroll methods are typically used to give the user some interactive feedback while a slider control is scrolling or while the scroll box is dragging across a slider control’s range of possible values. Here’s the prototype for the OnVScroll() method (the OnHScroll() method is exactly the same):

afx_msg void OnVScroll(UINT nSBCode, UINT nPos,
   CSliderCtrl* pScrollBar);

The first parameter, nSBCode, specifies one of 10 possible scrolling codes that tell your application what the user is doing with the slider control. The slider control has its own set of notification codes (which are just like the scroll bar codes), as listed in Table 3.10.

Table 3.10 The Notification Codes Used by the Slider Control in the OnHScroll() and OnVScroll() Methods

Code Meaning

TB_BOTTOM A user pressed the End key on the keyboard.
TB_ENDTRACK A user released a key, causing some virtual key code to be sent (WM_KEYUP).
TB_LINEDOWN A user pressed the down-arrow or right-arrow key on the keyboard.
TB_LINEUP A user pressed the up-arrow or left-arrow key on the keyboard.
TB_PAGEDOWN A user clicked the channel below or to the right of the slider or pressed the PageDown key.
TB_PAGEUP A user clicked the channel above or to the left of the slider or pressed the PageUp key.
TB_THUMBPOSITION A user released the left mouse button (WM_LBUTTONUP) after dragging the slider (TB_THUMBTRACK).
TB_THUMBTRACK A user dragged the slider.
TB_TOP A user pressed the Home key on the keyboard.


Note:  

When a user interacts with a slider control through the keyboard interface (and only then), the TB_BOTTOM, TB_LINEDOWN, TB_LINEUP, and TB_TOP codes are sent. Likewise, the TB_THUMBPOSITION and TB_THUMBTRACK codes are sent only when a user uses the mouse to drag the slider box. The other notification codes are sent no matter how a user interacts with the control.


The second parameter in the OnVScroll() or OnHScroll() method, nPos, reveals the current slider position when the notification code is either SB_THUMBPOSITION or SB_THUMBTRACK. If the nSBCode parameter is anything other than these two codes, nPos isn’t used.

The third and final parameter, pScrollBar, is a pointer to the slider control that sent the message. Even though a slider control isn’t a scroll bar, this pointer does refer to a slider control. When overriding the OnHScroll() or OnVScroll() method, simply typecast the point to a CSliderCtrl pointer, like this:

void OnVScroll(UINT nSBCode, UINT nPos, CSliderCtrl* pScrollBar)
{
   CSliderCtrl* pSlider = (CSliderCtrl*) pScrollBar;

//
// use the pointer...
//
}

CSliderCtrl Class Methods

The CSliderCtrl class offers a nice set of methods for manipulating the control and its data. Because the MFC help files shipped with your compiler contain all the CSliderCtrl class method declarations and detailed descriptions of their parameters, detailed descriptions aren’t given here, but I do provide an overview of each method so that you know what to look for when you need it.

The CSliderCtrl constructor, CSliderCtrl::CSliderCtrl(), allocates a slider control object that is initialized with the CSliderCtrl::Create() method to set attributes and ownership. The methods listed in Table 3.11 describe the methods used to get and set slider control attributes.

Table 3.11 CSliderCtrl Class Methods That Deal with Attributes

Method Description

GetChannelRect() Gets the size of the slider control’s channel.
GetLineSize() Gets the line size of a slider control.
GetNumTics() Gets the number of tick marks in a slider control.
GetPageSize() Gets the page size of a slider control.
GetPos() Gets the current position of the slider.
GetRange() Gets the minimum and maximum positions for a slider.
GetRangeMax() Gets the maximum position for a slider.
GetRangeMin() Gets the minimum position for a slider.
GetSelection() Gets the range of the current selection.
GetThumbRect() Gets the size of the slider control’s thumb.
GetTic() Gets the position of the specified tick mark.
GetTicArray() Gets the array of tick mark positions for a slider control.
GetTicPos() Gets the position of the specified tick mark, in client coordinates.
SetLineSize() Sets the line size of a slider control.
SetPageSize() Sets the page size of a slider control.
SetPos() Sets the current position of the slider.
SetRange() Sets the minimum and maximum positions for a slider.
SetRangeMax() Sets the maximum position for a slider.
SetRangeMin() Sets the minimum position for a slider.
SetSelection() Sets the selection range for a slider.
SetTic() Sets the position of the specified tick mark.
SetTicFreq() Sets the frequency of tick marks per slider control increment.



Table 3.12 shows the three operational methods a slider control can perform.

Table 3.12 CSliderCtrl Class Methods That Deal with Operations

Method Description

ClearSel() Clears the current selection from a slider control.
ClearTics() Removes the current tick marks from a slider control.
VerifyPos() Verifies that the position of a slider control is zero.

Creating and Initializing a Slider Control

A CSliderCtrl object, like most MFC objects, uses a two-step construction process. To create a slider control, perform the following steps:

1.  Allocate an instance of a CSliderCtrl object by calling the constructor CSliderCtrl::CSliderCtrl() using the C++ keyword new.
2.  Initialize the CSliderCtrl object and attach a Windows slider common control to it with the CSliderCtrl::Create() method to set slider parameters and styles.

For example, a CSliderCtrl object is allocated, and a pointer to the CSliderCtrl object is returned with this code:

CSliderCtrl* pMySlider = new CSliderCtrl;

The pointer pMySlider must then be initialized with a call to the CSliderCtrl::Create() method. This method is declared as follows:

BOOL Create(DWORD dwStyle, const RECT& rect,
            CWnd* pParentWnd, UINT nID);

The first parameter, dwStyle, specifies the window style for the slider control. This can be any combination of the general SDK window styles and the special slider control styles listed in Table 3.8, earlier in this chapter. The second parameter, rect, is the rectangle specifying the size and position of the control. The parameter pParentWnd is a pointer to the owner of the control, and nID is the control ID used by the parent to communicate with the slider control.

Sample Program: Slider Controls (SLIDER1)

The sample program SLIDER1 is just like sample program SPIN1 except that it uses sliders instead of spin controls, and it creates and displays three slider controls in a frame window (see Figure 3.4).


Figure 3.4  The SLIDER1 frame window with three child slider controls.

Sliders don’t respond to the system-wide scroll bar size change messages like scroll bars and spin controls do. Also, slider controls are much more independent than their scroll bar cousins. They update their own current positions and are smart enough to know how much to move for page change and line change notifications. To tell the sliders how much you want them to change for the notifications, use the following code in the CMainWnd::CreateChildControls() method (which includes the tick frequency):

// Set tick frequency
m_pSlider1->SetTicFreq(8);
m_pSlider2->SetTicFreq(8);
m_pSlider3->SetTicFreq(8);

// Set page size
m_pSlider1->SetPageSize(8);
m_pSlider2->SetPageSize(8);
m_pSlider3->SetPageSize(8);

// Set line size
m_pSlider1->SetLineSize(1);
m_pSlider2->SetLineSize(1);
m_pSlider3->SetLineSize(1);

This makes a big difference between the code for a scroll bar and the code for a slider in the SLIDER1 program’s CMainWnd::OnHScroll() method, thanks to the automatic tracking of the slider control. Here is the entire function that handles scrolling code:

void CMainWnd::OnHScroll(UINT nSBCode, UINT nPos,
                         CScrollBar* pScrollBar)
{
   // *Much* simpler than a scroll bar!

   // Change to the new color
   UpdateClientColor();

   // call inherited handler
   CMainFrame::OnHScroll(nSBCode, nPos, pScrollBar);

}

If you’ve ever written scrollbar code, you’ll see instantly that the slider control is a lot more user-friendly than a scroll bar—and makes for much easier coding!

Sample Program: SLIDER1

Next let’s take a look at the sample program SLIDER1. This simple program creates and displays three slider controls in a frame window (refer to Figure 3.4), and it’s very similar in design to the sample program SPIN1 that you saw earlier in this chapter. The SLIDER1 program uses three slider controls to set the RGB color components of the client area window color.

Examining the SLIDER1 Application Header (SLIDER1.H)

The header file for the SLIDER1 program begins by defining window styles for the frame window and its child controls:

// Main window style
#define WS_VISCHILD (WS_VISIBLE | WS_CHILD)

// Define a slider control window style
#define TBS_COLOR  \
   (TBS_HORZ | TBS_AUTOTICKS | WS_VISCHILD | WS_TABSTOP)

// Static control style
#define SS_STATIC (WS_VISCHILD | SS_CENTER)

Then the control IDs for the child window controls in this program are defined, as are the two classes used in this application: CSliderApp and CMainWnd.

The application class CSliderApp is almost exactly like class CspinApp, which you saw earlier. The CMainWnd class, derived from CMainFrame, contains pointers to the child windows as class data members. These child windows consist of three slider controls and three static controls. The class also provides the UpdateClientColor() helper method for changing the frame window’s client area color as well as these two message-handler methods:

// Message handlers
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnHScroll(UINT nSBCode, UINT nPos,
   CScrollBar* pScrollBar);
afx_msg void OnSize(UINT nType, int cx, int cy);

Finally, the DECLARE_MESSAGE_MAP() macro is used to set up message handling for the class.

Implementing the SLIDER1 Program (SLIDER1.CPP)

The first order of business is setting up the message map for the CMainWnd class. This message map contains four entries that correspond to the four message-handler method prototypes given in the class definition:

// Message map for CMainWnd
BEGIN_MESSAGE_MAP(CMainWnd, CMainFrame)
   ON_WM_ERASEBKGND()
   ON_WM_HSCROLL()
   ON_WM_SIZE()
END_MESSAGE_MAP()



CMainWnd::CreateChildControls() allocates and initializes the child controls. The sliders then receive a scroll range of 0 to 255, the possible range of byte values used by the RGB macro (that macro is used to change the frame window’s client area color later):

// Set slider ranges
m_pSlider1->SetRange(0, 255);
m_pSlider2->SetRange(0, 255);
m_pSlider3->SetRange(0, 255);

As a final step in initializing the spin controls, their individual current positions are set to the halfway position, at 128:

// Set current positions
m_pSlider1->SetPos(128);
m_pSlider1->SetPos(128);
m_pSlider1->SetPos(128);

The CMainWnd::OnSize() message handler first calls the inherited method that it overrides, then resizes each of the sliders by calling the CSliderCtrl::SetWindowPos() method, and finally calls CMainWnd::UpdateClientColor() to repaint the client area in the color specified by the current slider positions. Listing 3.1 shows how it’s done.

Listing 3.1 Resizing the Slider Controls Along with the Window


///////////////////////////////////////////////////////////////////
// CMainWnd::OnSize()

void CMainWnd::OnSize(UINT nType, int cx, int cy)
{
   // Call inherited method
   CWnd::OnSize(nType, cx, cy);

   // set some initial positions
   int nHeight = 20;
   int cyTop   = 10;

   // Resize the color sliders
   m_pSlider1->SetWindowPos(0, 10, cyTop, cx - 20, nHeight,
      SWP_SHOWWINDOW);
   cyTop += nHeight * 2;

   m_pSlider2->SetWindowPos(0, 10, cyTop, cx - 20, nHeight,
      SWP_SHOWWINDOW);
   cyTop += nHeight * 2;

   m_pSlider3->SetWindowPos(0, 10, cyTop, cx - 20, nHeight,
      SWP_SHOWWINDOW);
   cyTop += nHeight * 2;

   // Resize the static control
   m_pStatic1->SetWindowPos(0, 10, cyTop, cx - 20,
      m_nTextHeight, SWP_SHOWWINDOW);

   // Repaint the window at the new size
   UpdateClientColor();
}

To get the current position of the sliders, the UpdateClientColor() method calls CSlider::GetPos() for each, like this:

// Get the current scroll position
nRed   = m_pSlider1->GetPos();
nGreen = m_pSlider2->GetPos();
nBlue  = m_pSlider3->GetPos();

This is followed by some informative text that I set into each static child window. The CString::Format() method takes care of the dirty work for formatting the string:

// Display current RGB color as a text string
CString szText;
szText.Format(_T(“RGB(%d, %d, %d)”), nRed, nGreen, nBlue);
m_pStatic1->SetWindowText(szText);

Progress Bar Controls: Class CProgressCtrl

A progress bar control is a window that provides visual feedback to a user during a lengthy application operation. Because a progress bar control simply keeps a user apprised of an operation’s progress, the progress bar is typically for output only.

Like a slider control, a progress bar control has a range and a current position. The range specifies the length of some operation, and the current position represents how far along the operation has come at that time. Using these two values, the percentage of fill for the control is determined automatically.

A typical use of the progress bar control is for relaying information about the progress of file operations on a disk (see Figure 3.5).


Figure 3.5  Relaying information about the progress of a disk operation with a progress bar.

MFC provides the services of a Windows progress bar common control in the class CProgressCtrl, which is derived directly from CWnd and inherits all the functionality of CWnd. A progress bar control can be created as a child control of any window by writing code; it can also be defined in a dialog resource template.

CProgressCtrl Class Methods

The CProgressCtrl class offers a minimal set of methods for manipulating the control and its data. The constructor, CProgressCtrl::CProgressCtrl(), allocates a CProgressCtrl object that is initialized with the CProgressCtrl::Create() method. Table 3.13 describes the class’s methods.

Table 3.13 CProgressCtrl Class Methods

Method Description

OffsetPos() Advances the current position of a progress bar control by a specified increment and redraws the bar to show the new position.
SetPos() Sets the current position for a progress bar control and redraws the bar to show the new position.
SetRange() Sets the minimum and maximum ranges for a progress bar control and redraws the bar to show the new ranges.
SetStep() Specifies the step increment for a progress bar control.
StepIt() Advances the current position for a progress bar control by the step increment and redraws the bar to show the new position.

Creating and Initializing a CProgressCtrl Object

To create a CProgressCtrl object, you use the two-step construction process typical of MFC:

1.  Call the class constructor CProgressCtrl::CProgressCtrl() to allocate the object.
2.  Initialize the CProgressCtrl object and attach an actual Windows progress common control to it with a call to the CProgressCtrl::Create() method.

The prototype for the CProgressCtrl::Create() method is shown here:

BOOL Create(DWORD dwStyle, const RECT& rect,
            CWnd* pParentWnd, UINT nID);

In this syntax, the parameters are defined as follows:

  dwStyle Specifies the window style for the control. This can be any combination of the general window styles.
  rect The rectangle specifying the size and position of the control.
  pParentWnd A pointer to the owner of the control.
  nID The control ID used by the parent to communicate with the control.



Using a Progress Control

The only necessary settings for a progress control are the range and current position. The range represents the entire duration of the operation. The current position represents the progress that your application has made toward completing the operation. Any changes to the range or position cause the progress control to redraw itself.

The default range for a progress control is from 0 to 100, with the default initial position set to zero. Use the SetRange() method to change the range of the progress control; use the SetPos() method to set the current position. Alternatively, you can change the position by a preset amount by calling the SetStep() method to set an increment amount for the control (10 by default) and then calling the StepIt() method to change the position.


Note:  

The StepIt() method wraps around to the minimum range if the maximum is exceeded. The OffsetPos() method, however, doesn’t wrap back around to the minimum value—instead, the new position is adjusted to remain within the control’s specified range.


In the TAB1 program I demonstrate a progress bar control on the third tab, creating the progress bar like this:

// Create the progress bar control
if (!m_ctlProgress.Create(
      WS_CHILD | WS_VISIBLE | WS_BORDER,
      CRect(0,0,0,0), &m_ctlTab, IDC_PROGRESSCTRL))
{
   TRACE0(_T(“Problem creating progress bar control!”));
   return FALSE;
}

Next, set the lower and upper limits of the progress range with a call to CProgressCtrl::SetRange():

m_ctlProgress.SetRange(0, 100);

To force the visual aspect of progress occurring, I simply call the CProgressCtrl::SetPos() method, like this:

// Make some progress...
for (int i = 0; i < 100; i++)
{
   m_ctlProgress.SetPos(i);
   this->Wait(20);
}

Image Lists: Class CImageList

An image list maintains an array of images. Each image is the same size, and each element in the list is referred to by its zero-based index. To efficiently handle large numbers of icons or bitmaps, all images in an image list are placed into a single memory bitmap stored in DDB format. This bitmap has the same height as the images in the list; all the list’s images are contained side by side horizontally, usually making a very short, wide bitmap.

An image list can also include a monochrome bitmap mask used to draw images transparently. Win32 API image list functions give you the ability to draw images, replace images, merge images, add and remove images, drag images, and create and destroy image lists. This functionality is used by other common controls that make internal use of image lists, including list view, tree view, and tab controls. MFC provides the services of a Windows image list common control in the class CImageList, which is derived directly from CObject.

CImageList Class Methods

The CImageList class offers a complete set of methods for manipulating the images stored in a control. The CImageList constructor, CImageList::CImageList(), allocates a CImageList object that’s initialized with the CImageList::Create() method. Table 3.14 describes the methods provided by CImageList.

Table 3.14 CImageList Class Methods

Method Description

Add() Adds an image (or multiple images) to an image list.
Attach() Attaches an image list to a CImageList object.
BeginDrag() Begins dragging an image.
DeleteImageList() Deletes an image list.
Detach() Detaches an image list object from a CImageList object and returns a handle to an image list.
DragEnter() Locks the window to prevent updates during a drag operation and displays a drag image at the specified location.
DragLeave() Unlocks the window and hides the drag image so that the window can be updated.
DragMove() Moves the image being dragged during a drag-and-drop operation.
DragShowNolock() Shows or hides the drag image during a drag operation without locking the window.
Draw() Draws the image being dragged during a drag-and-drop operation.
EndDrag() Ends a drag operation.
ExtractIcon() Creates an icon based on an image and mask in an image list.
GetBkColor() Gets the current background color for an image list.
GetDragImage() Gets the temporary image list used for dragging.
GetImageCount() Gets the number of images in an image list.
GetImageInfo() Gets information about an image.
GetSafeHandle() Gets the underlying Windows image list stored in m_hImageList.
Read() Reads an image list from an archive.
Remove() Removes an image from an image list.
Replace() Replaces an image in an image list with a new image.
SetBkColor() Sets the background color for an image list.
SetDragCursorImage() Creates a new drag image.
SetOverlayImage() Adds the zero-based index of an image to the list of images to be used as overlay masks.
Write() Writes an image list to an archive.



Creating and Initializing a CImageList Control

To create a CImageList object, you use the two-step construction process typical of MFC:

1.  Call the class constructor CImageList::CImageList() to allocate the object.
2.  Initialize the CImageList object and attach an actual Windows image list common control to it with a call to the CImageList::Create() method.

There are four overloaded prototypes for the CImageList::Create() method:

// 1
BOOL Create(int cx, int cy, BOOL bMask, int nInitial, int nGrow);

// 2
BOOL Create(UINT nBitmapID, int cx, int nGrow, COLORREF crMask);

// 3
BOOL Create(LPCTSTR lpszBitmapID, int cx, int nGrow,
   COLORREF crMask);

// 4
BOOL Create(CImageList& imagelist1, int nImage1,
   CImageList& imagelist2, int nImage2, int dx, int dy);

The parameters used by these prototypes are listed in Table 3.15.

Table 3.15 The Parameters Used by the CImageList Control

Parameter Description

cx, cy Width and height of each image, in pixels.
dx, dy Width and height of each image, in pixels (same as cx and cy).
bMask A Boolean flag that specifies whether an image contains a monochrome mask.
nInitial The number of images initially contained in an image list.
nGrow The number of new images a resized image list can contain.
nBitmapID The resource ID of a bitmap to be associated with an image list.
crMask The color used to generate an image mask. Each pixel of this color in the specified bitmap is changed to black.
lpszBitmapID A string that contains the resource IDs of all images stored in an image list.
imagelist1 A pointer to another CImageList object.
nImage1 The number of images contained in imagelist1.
imagelist2 A pointer to another CImageList object.
nImage2 The number of images contained in imagelist2.

List View Controls: Class CListCtrl

A list view control is a window that provides several ways of arranging and displaying a list of items. Each item is made up of an icon and a label. Unlike the list box control, a list view control can display list items using four different views, and the current view is specified by the control’s current window style. Table 3.16 describes the four view styles provided by the list view control.

Table 3.16 The Four Types of Views Supported by the List View Common Control

View Description

Icon view In this view, list items are displayed as full-sized icons with labels below them, as specified by the LVS_ICON window style. In this view, a user can drag list items to any location in the list view window.
Small icon view In this view, list items are displayed as small icons with the labels to their right, as specified by the LVS_SMALLICON window style. In this view, a user can drag list items to any location in the list view window.
List view In this view, list items are displayed as small icons with labels to their right, as specified by the LVS_LIST window style. In this view, items are arranged and fixed in columns; they can’t be dragged to any other list view location.
Report view In this view, list items are displayed each on its own line, with information arranged in columns as specified by the LVS_REPORT window style. The left column displays a small icon and a label; the columns that follow contain application-specific subitems. Each column uses a Win32 header common control unless the LVS_NOCOLUMNHEADER window style is also specified.

As you’ll see later in this chapter, other window styles enable you to manage a list view control’s visual and functional aspects. Figure 3.6 shows four instances of Explorer, using the four view styles.


Figure 3.6  Four instances of Explorer, showing the four view styles.

MFC provides the services of a Windows list view common control in the class CListCtrl, which is derived directly from CWnd and inherits all the functionality of CWnd.

A list view control can be created as a child control of any window by writing code; it can also be defined in a dialog resource template. A list view control sends Windows notification messages to its owner (usually a CDialog-derived class), and these messages can be trapped and handled by writing message map entries and message-handler methods for each message. These message map entries and methods are implemented in the list view control’s parent class.



List View Control Styles

Like all windows, list view controls can use the general window styles available to CWnd. In addition, list view controls use the list view styles listed in Table 3.17 (as defined in AFXCMN.H). A list view control’s styles determine its appearance and operations. Style bits are typically set when the control is initialized with the CListCtrl::Create() method.


Tip:  

To retrieve the style bits present in a control, use the Windows API function GetWindowLong(). To change the style bits after the control has been initialized, use the corresponding Windows API function SetWindowLong().


Table 3.17 The Window Styles Defined for a List View Common Control

Style Macro Meaning

LVS_ALIGNLEFT Items are left-aligned in icon and small icon view.
LVS_ALIGNTOP Items are aligned with the top of the control in icon and small icon view.
LVS_AUTOARRANGE Icons are automatically arranged in icon view and small icon view.
LVS_EDITLABELS Allows item text to be edited in place.
LVS_ICON Icon view.
LVS_LIST List view.
LVS_NOCOLUMNHEADER No column header is displayed in report view.
LVS_NOLABELWRAP Item text is displayed on a single line in icon view.
LVS_NOSCROLL Disables scrolling.
LVS_NOSORTHEADER Column headers don’t function as buttons.
LVS_OWNERDRAWFIXED Enables the owner window to paint items as desired while in report view.
LVS_REPORT Report view.
LVS_SHAREIMAGELISTS Enables image lists to be used by multiple list view controls.
LVS_SINGLESEL Allows only one item at a time to be selected.
LVS_SMALLICON Small icon view.
LVS_SORTASCENDING Sorts items in ascending order based on item text.
LVS_SORTDESCENDING Sorts items in descending order based on item text.

In addition, many styles defined for the new Windows common controls can be used (see Table 3.18). These styles determine how a common control positions and resizes itself.

Table 3.18 Windows Common Control Window Styles Used by a List View Control

Style Macro Meaning

CCS_BOTTOM The control aligns itself at the bottom of the parent window’s client area and sizes itself to the width of its parent window’s client area.
CCS_NODIVIDER Prevents a two-pixel highlight from being drawn at the top of the control.
CCS_NOHILITE Prevents a one-pixel highlight from being drawn at the top of the control.
CCS_NOMOVEY Causes the control to resize and move itself horizontally (but not vertically) in response to a WM_SIZE message (default).
CCS_NOPARENTALIGN Prevents the control from automatically aligning to the top or bottom of the parent window.
CCS_NORESIZE Forces a control to use the width and height specified when created or resized.
CCS_TOP The control aligns itself at the top of the parent window’s client area and sizes itself to the width of its parent window’s client area.

Image Lists and the List View Control

The icons used by list view items are stored as image lists; there are three image lists available to a list view control:

  Large image list An image list that contains images of the full-sized icons used by the LVS_ICON list view style.
  Small image list An image list that contains the images of the small icons used by views that don’t have the LVS_ICON list view style.
  State image list An image list that can contain state images that can appear next to an item’s icon. These images are typically used to denote some application-specific state.

The large and small icon image lists should contain an icon for each type of item in the list. These lists are created individually as needed, and each uses the same index values. This arrangement means that images in the large icon list, for example, should correspond one to one with the images in the small icon list and the state icon list.



If you use a state image list for a list view control, the control reserves space for the state image just to the left of the icon for each list item.


Note:  

The large and small icon image lists can also contain overlay images that can be superimposed on list item icons. Because of a 4-bit indexing scheme for overlay images, overlay images must be stored within the first 15 images in a list.


List View Items and Subitems

A list view control contains a list of items; each item consists of four parts:

  An icon
  A label
  A current state
  An application-defined value

Each item can also contain strings called subitems. These subitems are used in the report view, and each subitem is displayed in its own column. You can use the CListCtrl methods to add, modify, retrieve, find, and delete list view items.


Note:  

Every item in a list view control must have the same number of subitems, even if the items represent different types of data.


A list view item or subitem is defined with a Windows LV_ITEM structure, which looks like this:

typedef struct _LV_ITEM
{
   UINT   mask;
   int    iItem;
   int    iSubItem;
   UINT   state;
   UINT   stateMask;
   LPSTR  pszText;
   int    cchTextMax;
   int    iImage;
   LPARAM lParam;
}
LV_ITEM;

The data members of this structure are as follows:

  mask A set of bit flags specifying the members of the LV_ITEM structure that contain valid data or that need to be filled in (see Table 3.19).
  iItem The zero-based index of an item.
  iSubItem The one-based index of a subitem.
  state The current state of an item.
  stateMask Specifies the bits of the state member that are valid.
  pszText A pointer to a string that contains the item text if the structure specifies item attributes.
  cchTextMax The size of the buffer pointed to by the pszText member.
  iImage The index of an item’s icon in the icon and small icon image lists.
  lParam An application-defined 32-bit value to associate with the item.
Table 3.19 The Bit Flags Used for the LV_ITEM mask Member

Value Meaning

LVIF_TEXT The pszText member is valid.
LVIF_IMAGE The iImage member is valid.
LVIF_PARAM The lParam member is valid.
LVIF_STATE The state member is valid.
LVIF_DI_SETITEM Windows should store the requested list item information.

List View Notification Messages

Like most Windows common controls, a list view control sends WM_NOTIFY notification messages to its parent window. These messages can be trapped and handled by writing message handlers in the list view control’s parent class. Table 3.20 shows the notifications used by list view controls, as defined in COMMCTRL.H.

Table 3.20 Notification Messages Defined for List View Controls

Message Map Entry Meaning

LVN_BEGINDRAG A drag-and-drop operation involving the left mouse button is beginning.
LVN_BEGINLABELEDIT A label-editing operation is beginning.
LVN_BEGINRDRAG A drag-and-drop operation involving the right mouse button is beginning.
LVN_COLUMNCLICK A column was clicked.
LVN_DELETEALLITEMS A user has deleted all items from the control.
LVN_DELETEITEM A user has deleted a single item from the control.
LVN_ENDLABELEDIT A label-editing operation is ending.
LVN_GETDISPINFO A request for the parent window to provide information needed to display or sort a list view item.
LVN_INSERTITEM A new item was inserted.
LVN_ITEMCHANGED An item was changed.
LVN_ITEMCHANGING An item is changing.
LVN_KEYDOWN A key was pressed.
LVN_PEN Used for pen Windows (for systems with a pen and digitizer tablet).
LVN_SETDISPINFO Forces the parent to update display information for an item.

Creating and Initializing a CListCtrl Object

To create a CListCtrl object, you use the two-step construction process typical of MFC:

1.  Call the class constructor CListCtrl::CListCtrl() to allocate the object.
2.  Initialize the CListCtrl object and attach an actual Windows list view common control to it with a call to the CListCtrl::Create() method.

The prototype for the CListCtrl::Create() method is shown here:

BOOL Create(DWORD dwStyle, const RECT& rect,
            CWnd* pParentWnd, UINT nID);

In this syntax, the parameters are as follows:

  dwStyle Specifies the combination of styles used by a list control.
  rect Specifies a list control’s size and position.
  pParentWnd Specifies the list control’s parent window.
  nID Specifies the control identifier for a list control.



The dwStyle parameter specifies the styles used by a list control, which can be any of the values listed in Table 3.17 (earlier in this chapter).


Note:  

MFC encapsulates the list control in the view class CListView, allowing you to take advantage of the benefits of an integrated list view control by allowing you to typecast a CListView object to a CListCtrl object at runtime.


Using the List View Control

Using the list view control generally requires several steps, including these:

1.  Attaching the image list(s).
2.  Adding columns for report view.
3.  Adding items to a CListCtrl object.
4.  Overriding the inherited OnChildNotify() method to handle WM_NOTIFY messages.

The following sections look at each of these items a little more closely.

Attaching the Image List(s)

If the list view control you’re creating uses the LVS_ICON style, you’ll need image lists for the list view items. Use the CImageList class to create an image list (or lists) for the list view to display. Next, call the CListCtrl::SetImageList() for all image lists used by the control.

Adding Columns for Report View

Columns can be used only with the LVS_REPORT style, which is the report view. The report view typically uses the header common control (CHeaderCtrl) to allow users to resize the column. Adding columns is easy: Simply initialize an LV_COLUMN structure and call the InsertColumn() method to create each desired column. An LV_COLUMN structure is defined as follows:

typedef struct _LV_COLUMN
{
   UINT    mask;        // Specifies valid data members
   int     fmt;         // Column alignment specifier
   int     cx;          // Width of column, in pixels
   LPTSTR  pszText;     // Column heading
   int     cchTextMax;  // Character size of pszText
   int     iSubItem;    // Index of a subitem
}
LV_COLUMN;

The members for this structure are described following:

  mask Specifies which members of this structure contain valid information. This member can be zero, or one or more of the values listed in Table 3.21.
  fmt Specifies the alignment of the column heading and the subitem text in the column. This can be one of the following values: LVCFMT_CENTER (centered text), LVCFMT_LEFT (flush-left text), or LVCFMT_RIGHT (flush-right text).
  cx Specifies the pixel width of a column.
  pszText A pointer to a column’s heading text string.
  cchTextMax Specifies the number of characters in the buffer pointed to by the pszText member.
  iSubItem Specifies the index of subitem associated with the column.
Table 3.21 The Possible Values for the LV_COLUMN mask Member

Value Meaning

LVCF_FMT The fmt member is valid.
LVCF_SUBITEM The iSubItem member is valid.
LVCF_TEXT The pszText member is valid.
LVCF_WIDTH The cx member is valid.


Note:  

The LV_COLUMN structure is used with the LVM_GETCOLUMN, LVM_SETCOLUMN, LVM_INSERTCOLUMN, and LVM_DELETECOLUMN list view control messages. The CListCtrl class wraps these messages with the default handler methods GetColumn(), SetColumn(), InsertColumn(), and DeleteColumn().


Adding Items to a CListCtrl Object

Depending on the type of data that’s going into a list view control, you can call one of the overloaded InsertItem() methods. Each version of this method takes a different type of data, and the list view control manages the storage for list items. For example, Listing 3.2 shows a code fragment from the TREELIST program that adds the names of items found in the tree control (m_ctlTree) to a list control (m_ctlList).

Listing 3.2 Adding Items to a List Control


///////////////////////////////////////////////////////////////////
// CMainWnd::ShowChildren()

void CMainWnd::ShowChildren(HTREEITEM hti)
{
   m_ctlList.DeleteAllItems();

   HTREEITEM htiNext  = 0;
   HTREEITEM htiChild = m_ctlTree.GetChildItem(hti);

   if (htiChild)
   {
      // Add the child’s tree text to the list
      int i = 0;
      CString str = m_ctlTree.GetItemText(htiChild);

      m_ctlList.InsertItem(i, (LPCTSTR) str);
      htiNext = htiChild;

      // Add sibling tree text to the list
      while (TRUE)
      {
         htiNext = m_ctlTree.GetNextSiblingItem(htiNext);
         if (!htiNext) return;

         CString str = m_ctlTree.GetItemText(htiNext);
         i++;
         m_ctlList.InsertItem(i, (LPCTSTR) str);
      }
   }
}

Tree View Controls: Class CTreeCtrl

A tree view control is a window that provides a hierarchical view of some set of data, such as a directory structure on a disk. Each item in the tree is made up of a label and an optional bitmap. Each item can own a list of subitems; by clicking an item, a user can expand or collapse the tree to reveal or hide subitems. MFC provides the services of a Windows tree view common control in the class CTreeCtrl, which is derived directly from CWnd and inherits all the functionality of CWnd.

A tree view control can be created as a child control of any window by writing code, or it can be defined in a dialog resource template. A tree view control sends Windows notification messages to its owner (usually a CDialog-derived class), and these messages can be trapped and handled by writing message map entries and message-handler methods for each message. These message map entries and methods are implemented in the list view control’s parent class.

Tree View Control Styles

Like all windows, tree view controls can use the general window styles available to CWnd. In addition, tree view controls use the tree view styles listed in Table 3.22 (as defined in COMMCTRL.H). A list view control’s styles determine its appearance and operation. Style bits are typically set when the control is initialized with the CTreeCtrl::Create() method.

Table 3.22 The Window Styles Defined for a Tree View Control

Style Macro Meaning

TVS_HASLINES Child items have lines linking them to corresponding parent items.
TVS_LINESATROOT Child items have lines linking them to the root of the tree.
TVS_HASBUTTONS The tree has a button to the left of each parent item.
TVS_EDITLABELS Tree view item labels can be edited.
TVS_SHOWSELALWAYS A selected item will remain selected, even if the tree view control loses the input focus.
TVS_DISABLEDRAGDROP Prevents the tree view control from sending TVN_BEGINDRAG notification messages.



Tree View Notification Messages

Like most of the Windows common controls, a tree view control sends WM_NOTIFY notification messages to its parent window, which is usually a CDialog-derived class. These messages can be trapped and handled by writing message handlers in the list view control’s parent class. Table 3.23 shows the notification messages defined in COMMCTRL.H.

Table 3.23 Notification Messages used by CTreeCtrl Objects

Notification Meaning

TVN_BEGINDRAG A drag-and-drop operation has begun.
TVN_BEGINLABELEDIT In-place label editing has begun.
TVN_BEGINRDRAG A drag-and-drop operation, using the right mouse button, has begun.
TVN_DELETEITEM A specific item has been deleted.
TVN_ENDLABELEDIT In-place label editing has ended.
TVN_GETDISPINFO Gets information that the tree control requires to display an item.
TVN_ITEMEXPANDED A parent item’s list of child items was expanded or collapsed.
TVN_ITEMEXPANDING A parent item’s list of child items is about to be expanded or collapsed.
TVN_KEYDOWN A key was pressed down.
TVN_SELCHANGED The current selection has changed from one item to another.
TVN_SELCHANGING The selection is about to change from one item to another.
TVN_SETDISPINFO Updates the information maintained for an item.

CTreeCtrl Class Methods

The CTreeCtrl class offers a full set of methods for manipulating the control and its data. The CTreeCtrl constructor, CTreeCtrl::CTreeCtrl(), allocates a CTreeCtrl object that is initialized with the CTreeCtrl::Create() method to set attributes and ownership. Table 3.24 describes the methods used to get and set button control attributes, as well as methods that perform operations on the control and its data.

Table 3.24 CTreeCtrl Class Methods

Method Description

CreateDragImage() Creates a dragging bitmap for the specified tree view item.
DeleteAllItems() Deletes all items in a tree view control.
DeleteItem() Deletes a new item in a tree view control.
EditLabel() Edits a specified tree view item in place.
EnsureVisible() Ensures that a tree view item is visible in its tree view control.
Expand() Expands or collapses the child items of the specified tree view item.
GetChildItem() Gets the child of a specified tree view item.
GetCount() Gets the number of tree items associated with a tree view control.
GetDropHilightItem() Gets the target of a drag-and-drop operation.
GetEditControl() Gets the handle of the edit control used to edit the specified tree view item.
GetFirstVisibleItem() Gets the first visible item of the specified tree view item.
GetImageList() Gets the handle of the image list associated with a tree view control.
GetIndent() Gets the offset (in pixels) of a tree view item from its parent.
GetItem() Gets the attributes of a specified tree view item.
GetItemData() Gets the 32-bit application-specific value associated with an item.
GetItemImage() Gets the images associated with an item.
GetItemRect() Gets the bounding rectangle of a tree view item.
GetItemState() Gets the state of an item.
GetItemText() Gets the text of an item.
GetNextItem() Gets the next tree view item that matches a specified relationship.
GetNextSiblingItem() Gets the next sibling of the specified tree view item.
GetNextVisibleItem() Gets the next visible item of the specified tree view item.
GetParentItem() Gets the parent of the specified tree view item.
GetPrevSiblingItem() Gets the previous sibling of the specified tree view item.
GetPrevVisibleItem() Gets the previous visible item of the specified tree view item.
GetRootItem() Gets the root of the specified tree view item.
GetSelectedItem() Gets the currently selected tree view item.
GetVisibleCount() Gets the number of visible tree items associated with a tree view control.
HitTest() Gets the current position of the cursor related to the CTreeCtrl object.
InsertItem() Inserts a new item in a tree view control.
ItemHasChildren() Determines whether an item has child items.
Select() Selects, scrolls into view, or redraws a specified tree view item.
SelectDropTarget() Redraws the tree item as the target of a drag-and-drop operation.
SelectItem() Selects a specified tree view item.
SetImageList() Sets the handle of the image list associated with a tree view control.
SetIndent() Sets the offset (in pixels) of a tree view item from its parent.
SetItem() Sets the attributes of a specified tree view item.
SetItemData() Sets the 32-bit application-specific value associated with an item.
SetItemImage() Associates images with an item.
SetItemState() Sets the state of an item.
SetItemText() Sets the text of an item.
SortChildren() Sorts the children of a given parent item.
SortChildrenCB() Sorts the children of a given parent item using an application-defined sort function.



Creating and Initializing a Tree View Control

To create a CTreeCtrl object, you use the two-step construction process typical of MFC:

1.  Call the class constructor CTreeCtrl::CTreeCtrl() to allocate the object.
2.  Initialize the CTreeCtrl object and attach an actual Windows tree view common control to it with a call to the CTreeCtrl::Create() method.

The prototype for the CTreeCtrl::Create() method is shown here:

BOOL Create(DWORD dwStyle, const RECT& rect,
            CWnd* pParentWnd, UINT nID);

In this syntax, the parameters are as follows:

  dwStyle Specifies the window style for the control. This can be any combination of the general window styles and the special tree view styles listed in Table 3.22.
  rect The rectangle specifying the size and position of the control.
  pParentWnd A pointer to the owner of the control.
  nID The control ID used by the parent to communicate with the tree view control.

Using a CTreeCtrl Object

If a tree control is to use images, create and set an image list by calling SetImageList(). You can further initialize the control by calling SetIndent() to change the indentation. Changing the indentation is usually done once, when the control is first initialized, typically in OnInitDialog() for a dialog box or in OnInitialUpdate() for a view.

Use the InsertItem() method to add data items to the control. Each call to InsertItem() results in an item handle being returned for each item added to the tree. These handles should be saved for later use.

Use an ON_NOTIFY macro in the message map entry for control notifications in the parent class, or make the class more reusable by placing an ON_NOTIFY_REFLECT macro in the control window’s message map to let it handle its own notifications. The tree control’s notification messages are listed in Table 3.23, earlier in this chapter.

Sample Program: TREELIST.EXE

On the companion CD-ROM, you’ll find the program TREELIST.EXE. This program uses the tree view and list view control classes (see Figure 3.7).


Figure 3.7  The TREELIST program.

The TREELIST program uses a CTreeCtrl object and a CListCtrl object; the list displays the children (if any) of the currently selected tree item. Tree item labels can also be edited in place.

Tab Controls: Class CTabCtrl

A tab control is a GUI metaphor for the tabs found on file folders. A tab control is a window that can be divided into several pages, each of which typically has a set of controls. Each tab page provides a tab that, when clicked, displays its corresponding page. Figure 3.8 shows a typical tab window used for displaying various controls.


Figure 3.8  A typical tab control window.


Note:  

A tab control can also display buttons in place of tabs. Clicking a button should immediately perform a command instead of displaying a page.


MFC provides the services of a Windows tab common control in the class CTabCtrl, which is derived directly from CWnd and inherits all the functionality of CWnd. A tab control can be created as a child control of any window by writing code; it can also be defined in a dialog resource template.

A tab control sends Windows notification messages to its owner (usually a CDialog-derived class), and these messages can be trapped and handled by writing message map entries and message-handler methods for each message. These message map entries and methods are implemented in the tab control’s parent class.

Tab Control Styles

Like all windows, tab controls can use the general window styles available to CWnd. They can also use the additional styles listed in Table 3.25. A tab control’s styles determine its appearance and operations; style bits are typically set when the control is initialized with the CTabCtrl::Create() method.


Tip:  

To retrieve the style bits present in a control, use the Windows API function GetWindowLong(). To change the style bits after the control has been initialized, use the corresponding Windows API function SetWindowLong().


Table 3.25 The Window Styles Designed for Use with Tab Controls

Style Macro Meaning

TCS_BUTTONS Makes tabs appear as standard pushbuttons.
TCS_FIXEDWIDTH Makes all tabs the same width.
TCS_FOCUSNEVER Specifies that a tab never receives the input focus.
TCS_FOCUSONBUTTONDOWN A tab will receive the input focus when clicked (typically used only with the TCS_BUTTONS style).
TCS_FORCEICONLEFT Forces a tab’s icon to the left but leaves the tab label centered.
TCS_FORCELABELLEFT Left-aligns both the icon and label.
TCS_MULTILINE A tab control displays multiple rows of tabs to ensure that all tabs can be displayed at once.
TCS_OWNERDRAWFIXED The parent window draws the tabs for the control.
TCS_RAGGEDRIGHT A default style that doesn’t force each row of tabs to fill the width of the control.
TCS_RIGHTJUSTIFY Right-justifies tabs.
TCS_SHAREIMAGELISTS A tab control’s image lists aren’t destroyed with the control. This allows multiple controls to use the same image lists.
TCS_SINGLELINE Displays all tabs in a single row.
TCS_TABS The standard tab style; specifies that tabs appear as tabs (as opposed to buttons) and that a border is drawn around the display area.
TCS_TOOLTIPS The tab control uses a ToolTip control.



Tab Control Notification Messages

Like most Windows common controls, a tab control sends WM_NOTIFY notification messages to its parent window, which is usually a CDialog-derived class. Table 3.26 shows the notification messages used by the tab control, as defined in COMMCTRL.H.

Table 3.26 Notification Messages Used by the Tab Control

Notification Meaning

TCN_KEYDOWN A key was pressed.
TCN_SELCHANGE The currently selected tab has changed.
TCN_SELCHANGING The currently selected tab is about to change. By returning TRUE in response to this notification, you can prevent the selection from changing.


Tip:  

Use the GetCurSel() method to determine the currently selected tab.


CTabCtrl Class Methods

The CTabCtrl class offers a complete set of methods for manipulating the control and its data. The CTabCtrl constructor, CTabCtrl::CTabCtrl(), allocates a CTabCtrl object that is initialized with the CTabCtrl::Create() method to set attributes and ownership. The CTabCtrl class methods are described in Table 3.27.

Table 3.27 CTabCtrl Class Methods

Method Description

AdjustRect() Calculates a tab control’s display area given a window rectangle; alternatively, calculates the window rectangle that corresponds to a given display area.
DeleteAllItems() Removes all items from a tab control.
DeleteItem() Removes an item from a tab control.
DrawItem() Draws a specified tab control item.
GetCurFocus() Gets the tab control tab that has the input focus.
GetCurSel() Gets the currently selected tab control tab.
GetImageList() Gets the image list associated with a tab control.
GetItem() Gets information about a tab in a tab control.
GetItemCount() Gets the number of tabs in the tab control.
GetItemRect() Gets the bounding rectangle for a tab in a tab control.
GetRowCount() Gets the current number of rows of tabs in a tab control.
GetTooltips() Gets the handle of the ToolTip control associated with a tab control.
HitTest() Determines which tab (if any) is located at a specified screen position.
InsertItem() Inserts a new tab into a tab control.
RemoveImage() Removes an image from a tab control’s image list.
SetCurSel() Selects one of a tab control’s tabs.
SetImageList() Assigns an image list to a tab control.
SetItem() Sets some or all of a tab’s attributes.
SetItemSize() Sets the width and height of an item.
SetPadding() Sets the amount of padding around each tab’s icon and label in a tab control.
SetTooltips() Assigns a ToolTip control to a tab control.

The Tab Item Structure (TC_ITEM)

The GetItem(), SetItem(), and InsertItem() methods all take a parameter of type TC_ITEM. This is a new datatype for Win32 that specifies or receives the attributes of a tab. The TC_ITEM structure has the following form:

typedef struct _TC_ITEM
{
   UINT    mask;
   UINT    lpReserved1;  // reserved; do not use
   UINT    lpReserved2;  // reserved; do not use
   LPSTR   pszText;
   int     cchTextMax;
   int     iImage;
   LPARAM  lParam;
}
TC_ITEM;

The members of this structure are as follows:

  mask A value specifying which members to retrieve or set. This member can be all members (TCIF_ALL), zero (0), or one or more of the values listed in Table 3.28.
  pszText A pointer to a string containing the tab text.
  cchTextMax The size of the buffer pointed to by the pszText member.
  iImage The index into the tab control’s image list, or –1 if the tab has no image.
  lParam Application-defined data associated with the tab.
Table 3.28 Tab Item Structure mask Values

Value Description

TCIF_IMAGE The iImage member is valid.
TCIF_PARAM The lParam member is valid.
TCIF_RTLREADING Displays the text pointed to by pszText using right-to-left reading order when running on Hebrew or Arabic systems.
TCIF_TEXT The pszText member is valid.



Creating and Initializing a Tab Control

To create a CTabCtrl object, you use the two-step construction process typical of MFC:

1.  Call the class constructor CTabCtrl::CTabCtrl() to allocate the object.
2.  Initialize the CTabCtrl object and attach an actual Windows tab common control to it with a call to the CTabCtrl::Create() method.

The prototype for the CTabCtrl::Create() method is shown here:

BOOL Create(DWORD dwStyle, const RECT& rect,
            CWnd* pParentWnd, UINT nID);

In this syntax, the parameters are as follows:

  dwStyle Specifies the combination of styles used by the control.
  rect Specifies a control’s size and position.
  pParentWnd Specifies a control’s parent window.
  nID Specifies the control identifier for a control.


Note:  

As with other Windows controls, a tab control is created implicitly when used in a dialog box; a tab control is created explicitly when created in a nondialog window.


Using a Tab Control

After the CTabCtrl object is constructed, you add tabs to the tab control to complete its initialization. You are responsible for handling any tab notification messages that apply to your application.

Adding Tabs to a Tab Control

After constructing the CTabCtrl object, add tabs as needed by preparing a TC_ITEM structure and calling the CTabCtrl::InsertItem() method. Pass the TC_ITEM structure as a parameter as shown in this simple example:

// Initialize the TC_ITEM structures
TC_ITEM tci;

CString str = “Tab 1”;

tci.mask       = TCIF_TEXT;
tci.pszText    = (LPSTR)(LPCTSTR)str;
tci.cchTextMax = str.GetLength();

// Add this tab to the tab control
m_ctlTab.InsertItem(0, &tci);

Note this line of code in the preceding example:

tci.pszText = (LPSTR)(LPCTSTR)str;

The CString variable str uses the LPCTSTR operator to get a const pointer to the character string contained in the string object. The const is then cast away to the LPSTR expected by tci.pszText.

Trapping Tab Notification Messages

The final step in dealing with tab controls is handling any tab notifications for your application. A simple notification handler that fires when a user clicks the control is given in Listing 3.3. In this case, the hypothetical tab control (m_ctlTab) is assumed to be a class data member; this control is assumed to contain two tab items.

Listing 3.3 A Simple Tab Notification Message Handler


///////////////////////////////////////////////////////////////////
// CMainWnd::OnClickTabCtrl()

void  CMainWnd::OnClickTabCtrl(NMHDR* pNotifyStruct,
                               LRESULT* pResult)
{
   // Set the return code
   *pResult = 0;

   // Get the currently active tab
   int nCurTab = m_ctlTab.GetCurSel();
   // Perform some action in response to the tab becoming active
   switch (nCurTab)
   {
      case 0:
         MessageBeep(MB_ICONASTERISK);
         AfxMessageBox(“You activated Tab 1!”,
            MB_OK | MB_ICONINFORMATION);
         break;
      case 1:
         MessageBeep(MB_ICONASTERISK);
         AfxMessageBox(“You activated Tab 2!”,
            MB_OK | MB_ICONINFORMATION);
   }
}

The corresponding handler in the class’s message map looks something like this (where IDC_TABCTRL is assumed to be a valid control identifier for the tab control in question):

BEGIN_MESSAGE_MAP(CMainWnd, CMainFrame)
   // ...Other possible entries...
   ON_NOTIFY(NM_CLICK, IDC_TABCTRL, OnClickTabCtrl)
END_MESSAGE_MAP()

Animate Controls: Class CAnimateCtrl

An animation control is a rectangular window that displays an animation in Audio Video Interleaved (AVI) format, which is the standard video for Windows file format. Viewed simplistically, an AVI file is composed of a series of animation frames; each frame is a bitmap image. Figure 3.9 shows three frames from the AVI clip SPINTORI.AVI, which shows a spinning red object (run the program TAB1.EXE on the companion CD-ROM to see this in action).


Note:  

Nine bitmaps were used to create the SPINTORI.AVI file, and all were generated using the Persistence of Vision ray tracing tool kit (POVRAY). The POVRAY source files for the spinning tori animation are also included on the CD-ROM.



Figure 3.9  Three bitmaps from the SPINTORI.AVI animation file.

Animation controls can play only simple AVI clips, and they don’t support sound. In fact, the types of AVIs an animate control can play are quite limited, and must meet the following specifications:

  There must be only one video stream containing at least one frame.
  In addition to a single video stream, an AVI can also have a single audio stream, although audio is ignored by an animate control.
  The only type of data compression allowed for use with animate controls is Microsoft’s RLE8 compression. Uncompressed AVIs work well with the animate control, but can be quite large.
  A single palette must be used throughout the video stream.

The animate control allows full multithreading in your applications, which makes the control useful for keeping users entertained during lengthy operations—or at least to assure them that their system hasn’t locked up. In this capacity, the animation acts as a reassuring “don’t worry, we’re still working on it” element for the user. The animate control is a nice alternative to the progress bar control, especially when the remaining duration of an operation is unknown.

MFC provides the services of a Windows animate common control in the class CAnimateCtrl, which is derived directly from CWnd and inherits all the functionality of CWnd. An animate control can be created as a child control of any window by writing code; it can also be defined in a dialog resource template.

Animate Control Styles

Like all windows, animate controls can use the general window styles available to CWnd. In addition, they can use the animate control styles listed in Table 3.29 (as defined in COMMCTRL.H). An animate control’s styles determine its appearance and operations. Style bits are typically set when the control is initialized with the CAnimateCtrl::Create() method.

Table 3.29 The Window Styles Specific to an Animate Control

Style Description

ACS_AUTOPLAY Tells the control to play an AVI clip when it’s opened and to automatically loop the video playback indefinitely.
ACS_CENTER Centers the AVI clip in the control window.
ACS_TRANSPARENT The background color specified in the AVI clip is drawn as transparent.


Note:  

If the ACS_CENTER style isn’t specified, the animate control is resized to the size of the images in the video clip when the file is opened for reading.




Animate Control Notification Messages

Like most Windows common controls, an animate control sends WM_NOTIFY notification messages to its parent window. These messages can be trapped and handled by writing message map entries and message-handler methods implemented in the animate control’s owner class for each message. Table 3.30 shows the notification messages used by an animate control.

Table 3.30 Message Map Entries for Animate Control Notification Messages

Notification Meaning

ACN_START An animation control has started playing an AVI clip.
ACN_STOP An animation control has either finished playing or stopped playing an AVI clip.

CAnimateCtrl Class Methods

The CAnimateCtrl class offers a minimal set of methods. These methods are described in Table 3.31.

Table 3.31 CAnimate Class Methods

Method Description

Close Closes an open AVI clip.
Open Opens an AVI clip from a file or resource and displays the first frame.
Play Plays an AVI clip, leaving out any audio tracks that might be present (audio is ignored).
Seek Displays a selected single frame of an AVI clip.
Stop Stops playing an AVI clip.

Creating and Initializing an Animate Control

To create a CAnimateCtrl object, you use the two-step construction process typical of MFC:

1.  Call the class constructor CAnimateCtrl::CAnimateCtrl() to allocate the object.
2.  Initialize the CAnimateCtrl object and attach an actual Windows animate common control to it with a call to the CAnimateCtrl::Create() method.

The prototype for the CAnimateCtrl::Create() method is shown here:

BOOL Create(DWORD dwStyle, const RECT& rect,
            CWnd* pParentWnd, UINT nID);

In this syntax, the parameters are defined as follows:

  dwStyle Specifies the combination of styles used by a control.
  rect Specifies a control’s size and position.
  pParentWnd Specifies the control’s parent window.
  nID Specifies the control identifier for a control.

Using an Animate Control

After the CAnimateCtrl object is constructed, you open and play an AVI clip by performing the following steps:

  Open the AVI clip by calling the CAnimateCtrl::Open() method.
  Play the AVI by calling the CAnimateCtrl::Play() method.

For example, the TAB1 program plays an AVI clip like this, where m_ctlAnim is a CAnimateCtrl object:

// Open and play the AVI
if (m_ctlAnim.Open((LPCTSTR)“spintori.avi”))
{
   m_ctlAnim.ShowWindow(SW_SHOWNORMAL);
   m_ctlAnim.Play(0, (UINT)-1, (UINT)-1);
}

The following section looks at the big brother of the hearty edit control: the rich edit control.

Rich Edit Controls: Class CRichEditCtrl

A rich edit control is a window that, at first glance, looks very similar to a standard edit control. But a rich edit control has the additional benefits of letting users perform character and paragraph formatting, as well as embed OLE objects. Although the rich edit control provides the functionality, your application must implement the user interface components that allow users to perform formatting operations on the text.

The MFC classes CRichEditDoc, CRichEditView, and CRichEditCntrItem encapsulate the functionality of a rich edit control into full-featured classes for use in your document/view programs. Figure 3.10 shows the WordPad application that ships with Windows 95. Note that this application is mainly just a rich edit control with some GUI trappings (toolbar, status bar, and so on) wrapped in a nice document/view framework.

A rich edit control provides support for changing the character attributes of selected text, such as whether a character is displayed as bold, italicized, or with a certain font family and point size. A rich edit control also provides support for setting paragraph attributes, such as justification, margin size, and tab-stop values.


Note:  

Although the rich edit control provides the means for the formatting, your application must provide the user interface controls that users manipulate to actually format the text.



Figure 3.10  A typical rich edit control makes the WordPad application possible.

MFC provides the services of a Windows rich edit common control in the class CRichEditCtrl, which is derived directly from CWnd and inherits all the functionality of CWnd.

Although the CRichEditCtrl class isn’t derived from CEdit, a CRichEditCtrl object supports most of the operations and notification messages used for multiple-line edit controls. In fact, the default style for a rich edit control is single line, just as it is for an edit control; you must set the ES_MULTILINE window style to create a multiple-line rich edit control. This style makes it easy for applications that already make use of edit controls to be easily modified to use rich edit controls. Figure 3.11 shows the TAB1 program displaying rich text on the second tab.


Figure 3.11  Displaying rich text in the TAB1 program.


Note:  

Because the rich edit control is specific to Win32, the CRichEditCtrl class is available only to programs running under Windows 95, Windows NT version 3.51 or later, and Windows 3.1x with Win32s 1.3 or later.


A rich edit control can be created as a child control of any window by writing code; it can also be defined in a dialog resource template. A rich edit control sends Windows notification messages to its owner (usually a CDialog-derived class), and these messages can be trapped and handled by writing message map entries and message-handler methods for each message. These message map entries and methods are implemented in the rich edit control’s parent class.



Rich Edit Control Window Styles

Like all windows, rich edit controls can use the general window styles available to CWnd. Unlike other controls, the rich edit control defines no additional window styles.

The Character Format Structure (CHARFORMAT)

The CRichEditCtrl class’s GetDefaultCharFormat(), GetSelectionCharFormat(), SetDefaultCharFormat(), SetSelectionCharFormat(), and SetWordCharFormat() methods all take a parameter of type CHARFORMAT. This is a Win32 datatype (see Listing 3.4) that contains information about character formatting in a rich edit control.

Listing 3.4 The CHARFORMAT Structure


typedef struct _charformat
{
   UINT     cbSize;
   _WPAD    _wPad1;
   DWORD    dwMask;
   DWORD    dwEffects;
   LONG     yHeight;
   LONG     yOffset;
   COLORREF crTextColor;
   BYTE     bCharSet;
   BYTE     bPitchAndFamily;
   TCHAR    szFaceName[LF_FACESIZE];
   _WPAD    _wPad2;
}
CHARFORMAT;

The data members for this structure are as follows:

  cbSize Size in bytes of this structure. Must be set before passing the structure to the rich edit control.
  dwMask Members containing valid information or attributes to set. This member can be zero or one or more of the values listed in Table 3.32.
  dwEffects Character effects. This member can be a combination of the values listed in Table 3.33.
  yHeight Character height.
  yOffset Character offset from the baseline. If this member is positive, the character is a superscript; if it is negative, the character is a subscript.
  crTextColor Text color. This member is ignored if the CFE_AUTOCOLOR character effect is specified.
  bCharSet Character set value. Can be one of the values specified for the lfCharSet member of the LOGFONT structure.
  bPitchAndFamily Font family and pitch. This member is the same as the lfPitchAndFamily member of the LOGFONT structure.
  szFaceName NULL-terminated character array specifying the font face name.
Table 3.32 Possible CHARFORMAT dwMask Values

Value Meaning

CFM_BOLD The CFE_BOLD value of the dwEffects member is valid.
CFM_COLOR The crTextColor member and the CFE_AUTOCOLOR value of the dwEffects member are valid.
CFM_FACE The szFaceName member is valid.
CFM_ITALIC The CFE_ITALIC value of the dwEffects member is valid.
CFM_OFFSET The yOffset member is valid.
CFM_PROTECTED The CFE_PROTECTED value of the dwEffects member is valid.
CFM_SIZE The yHeight member is valid.
CFM_STRIKEOUT The CFE_STRIKEOUT value of the dwEffects member is valid.
CFM_UNDERLINE. The CFE_UNDERLINE value of the dwEffects member is valid.

Table 3.33 Possible Rich Edit Control Character Effects Values

Value Meaning

CFE_AUTOCOLOR The text color is the return value of GetSysColor
(COLOR_WINDOWTEXT).
CFE_BOLD Characters are bold.
CFE_ITALIC Characters are italic.
CFE_STRIKEOUT Characters are struck out.
CFE_UNDERLINE Characters are underlined.
CFE_PROTECTED Characters are protected; an attempt to modify them causes an EN_PROTECTED notification message.



The Paragraph Format Structure (PARAFORMAT)

Another Win32 structure, called PARAFORMAT, is used as a parameter with the GetParaFormat() and SetParaFormat() methods. The PARAFORMAT structure contains information about formatting attributes in a rich edit control and is shown in Listing 3.5.

Listing 3.5 The PARAFORMAT Structure


typedef struct _paraformat
{
   UINT cbSize;
   _WPAD _wPad1;
   DWORD dwMask;
   WORD  wNumbering;
   WORD  wReserved;
   LONG  dxStartIndent;
   LONG  dxRightIndent;
   LONG  dxOffset;
   WORD  wAlignment;
   SHORT cTabCount;
   LONG  rgxTabs[MAX_TAB_STOPS];
}
PARAFORMAT;

The data members for this structure are as follows:

  cbSize Size in bytes of this structure. Must be filled before passing to the rich edit control.
  dwMask Members containing valid information or attributes to set. This parameter can be zero or one or more of the values shown in Table 3.34.
  wNumbering Value specifying numbering options. This member can be zero or PFN_BULLET.
  dxStartIndent Indentation of the first line in the paragraph. If the paragraph formatting is being set and PFM_OFFSETINDENT is specified, this member is treated as a relative value that is added to the starting indentation of each affected paragraph.
  dxRightIndent Size of the right indentation, relative to the right margin.
  dxOffset Indentation of the second and subsequent lines, relative to the starting indentation. The first line is indented if this member is negative; it is outdented if this member is positive.
  wAlignment Value specifying the paragraph alignment. This member can be one of the values listed in Table 3.35.
  cTabCount Number of tab stops.
  rgxTabs Array of absolute tab-stop positions.
Table 3.34 Rich Edit Control Paragraph dwMask Values

Value Meaning

PFM_ALIGNMENT The wAlignment member is valid.
PFM_NUMBERING The wNumbering member is valid.
PFM_OFFSET The dxOffset member is valid.
PFM_OFFSETINDENT The dxStartIndent member is valid and specifies a relative value.
PFM_RIGHTINDENT The dxRightIndent member is valid.
PFM_STARTINDENT The dxStartIndent member is valid.
PFM_TABSTOPS The cTabStobs and rgxTabStops members are valid.

Table 3.35 Possible Rich Edit Control Paragraph Alignment Values

Value Meaning

PFA_LEFT Paragraphs are aligned with the left margin.
PFA_RIGHT Paragraphs are aligned with the right margin.
PFA_CENTER Paragraphs are centered.

CRichEditCtrl Class Methods

The CRichEditCtrl class offers a full set of methods for manipulating the control and its data. The CRichEditCtrl class is complex, and its methods can be broken out into several categories:

  Line-related methods
  Text-selection methods
  Text-formatting methods
  Editing methods
  Clipboard methods
  General-purpose methods

The following sections look at descriptions for all these CRichEditCtrl methods.

CRichEditCtrl Line-Related Methods

The CRichEditCtrl methods related to manipulating lines of text are described in Table 3.36.

Table 3.36 CRichEditCtrl Line-Related Methods

Method Description

GetLineCount() Retrieves the number of lines in this CRichEditCtrl object.
GetLine() Retrieves a line of text from this CRichEditCtrl object.
GetFirstVisibleLine() Determines the topmost visible line in this CRichEditCtrl object.
LineIndex() Retrieves the character index of a given line in this CRichEditCtrl object.
LineFromChar() Determines which line contains the given character.
LineLength() Retrieves the length of a given line in this CRichEditCtrl object.
LineScroll() Scrolls the text in this CRichEditCtrl object.



CRichEditCtrl Text-Selection Methods

The CRichEditCtrl methods related to selecting text, clearing or getting selected text, and so on, are described in Table 3.37.

Table 3.37 CRichEditCtrl Text-Selection Methods

Method Description

Clear() Clears the current selection.
GetSel() Gets the starting and ending positions of the current selection.
GetSelectionType() Retrieves the type of contents in the current selection.
GetSelText() Gets the text of the current selection.
HideSelection() Shows or hides the current selection.
ReplaceSel() Replaces the current selection with specified text.
SetSel() Sets the selection.

CRichEditCtrl Formatting Methods

The CRichEditCtrl methods related to formatting text are described in Table 3.38.

Table 3.38 CRichEditCtrl Formatting Methods

Method Description

GetDefaultCharFormat() Gets the current default character formatting attributes.
GetParaFormat() Gets the paragraph formatting attributes in the current selection.
GetSelectionCharFormat() Gets the character formatting attributes in the current selection.
SetDefaultCharFormat() Sets the current default character formatting attributes.
SetParaFormat() the paragraph formatting attributes in the current selection.
SetSelectionCharFormat() Sets the character formatting attributes in the current selection.
SetWordCharFormat() Sets the character formatting attributes in the current word.

CRichEditCtrl Editing Methods

The CRichEditCtrl methods related to editing text are described in Table 3.39.

Table 3.39 CRichEditCtrl Editing Methods

Method Description

CanUndo() Determines whether an editing operation can be undone.
EmptyUndoBuffer() Resets a CRichEditCtrl object’s undo flag.
StreamIn() Inserts text from an input stream.
StreamOut() Stores text from a CRichEditCtrl object in an output stream.
Undo() Undoes the last editing operation.

CRichEditCtrl Clipboard Methods

The CRichEditCtrl methods that allow a rich edit control to interact with the Windows Clipboard are described in Table 3.40.

Table 3.40 CRichEditCtrl Clipboard Methods

Method Description

CanPaste() Checks to see whether the contents of the Clipboard can be pasted into a rich edit control.
Copy() Copies the current selection to the Clipboard.
Cut() Cuts the current selection to the Clipboard.
Paste() Inserts the contents of the Clipboard into a rich edit control.
PasteSpecial() Inserts the contents of the Clipboard into a rich edit control using the specified data format.



CRichEditCtrl General-Purpose Methods

General-purpose CRichEditCtrl methods are described in Table 3.41.

Table 3.41 General-Purpose Methods

Method Description

DisplayBand() Displays a portion of the contents of a CRichEditCtrl object.
FindText() Locates text within a CRichEditCtrl object.
FormatRange() Formats a range of text for the target output device.
GetCharPos() Gets the location of a given character within this CRichEditCtrl object.
GetEventMask() Gets the event mask for a CRichEditCtrl object.
GetLimitText() Gets the limit on the amount of text a user can enter into a CRichEditCtrl object.
GetModify() Determines whether the contents of a CRichEditCtrl object have been modified since last saved.
GetRect() Gets the formatting rectangle for a CRichEditCtrl object.
GetTextLength() Gets the length of the text in a CRichEditCtrl object.
LimitText() Limits the amount of text a user can enter into the CRichEditCtrl object.
RequestResize() Forces a CRichEditCtrl object to send notifications to its parent window requesting that it be resized.
SetBackgroundColor() Sets the background color in a CRichEditCtrl object.
SetEventMask() Sets the event mask for a CRichEditCtrl object.
SetModify() Sets or clears the modification flag for a CRichEditCtrl object.
SetOptions() Sets the options for a CRichEditCtrl object.
SetReadOnly() Sets the read-only option for a CRichEditCtrl object.
SetRect() Sets the formatting rectangle for a CRichEditCtrl object.
SetTargetDevice() Sets the target output device for a CRichEditCtrl object.

Creating and Initializing a Rich Edit Control

To create a CRichEditCtrl object, you use the two-step construction process typical of MFC:

1.  Call the class constructor CRichEditCtrl::CRichEditCtrl() to allocate the object.
2.  Initialize the CRichEditCtrl object and attach an actual Windows rich edit common control to it with a call to the CRichEditCtrl::Create() method.

The prototype for the CRichEditCtrl::Create() method is shown here:

BOOL Create(DWORD dwStyle, const RECT& rect,
            CWnd* pParentWnd, UINT nID);

In this syntax, the parameters are defined as follows:

  dwStyle Specifies the combination of styles used by a control.
  rect Specifies a control’s size and position.
  pParentWnd Specifies the control’s parent window.
  nID Specifies the control identifier for a control.

Using a Rich Edit Control

After constructing the CRichEditCtrl object, you add text and format it as desired by doing the following:

1.  Prepare PARAFORMAT and CHARFORMAT structures to define paragraph and character formatting specifics.
2.  Call on the CRichEditCtrl methods that use these structures to perform their magic.

For example, the sample program TAB1 (located on the companion CD-ROM) uses the code shown in Listing 3.6 to set the character formatting specifics for a given CHARFORMAT structure.

Listing 3.6 Initializing a CHARFORMAT Structure


/////////////////////////////////////////////////////////////////////
// CMainWnd::SetStyleHeading1()

void CMainWnd::SetStyleHeading1(CHARFORMAT& cf)
{
   cf.cbSize          = sizeof(CHARFORMAT);
   cf.dwMask          = CFM_COLOR | CFM_FACE | CFM_SIZE |
                        CFM_ITALIC | CFM_BOLD;
   cf.dwEffects       = CFE_BOLD | CFE_ITALIC;
   cf.yHeight         = 500;
   cf.crTextColor     = crRed;            // from colors.h
   cf.bCharSet        = ANSI_CHARSET;
   cf.bPitchAndFamily = FF_ROMAN;

   lstrcpy(cf.szFaceName, “Times New Roman”);
}

As you can see, you can easily control the characteristics of a display font with the CHARFORMAT structure. With a little extra work you can make various formatting changes an interactive experience for the user—try creating various functions and attaching them to user-interface elements such as buttons on a toolbar.

Summary

In this chapter you’ve examined several MFC classes that encapsulate the Win32 common controls. You’ve explored the messages and methods of these classes, and seen how to use them in an application context. Next, you’ll take a closer look at graphics programming in MFC.